/** @file    WindowsMobileBundle.cpp
 *  @author  Alessandro Polo
 *  @version $Id: WindowsMobileBundle.cpp 2884 2010-08-10 00:31:45Z alex $
 *  @brief
 * File containing methods for the wosh::devices::WindowsMobileBundle class.
 * The header for this class can be found in WindowsMobileBundle.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "WindowsMobileBundle.h"

 #include "WindowsMobileImpl.h"

 #include <core/ObjectAllocator.h>
 #include <core/MethodsCommon.h>
 #include <core/MessageFilter.h>
 #include <core/Fact.h>
 #include <core/Table.h>
 #include <core/ThreadManager.h>
 #include <core/SecurityManager.h>
 #include <core/UserManager.h>
 #include <core/DeviceManager.h>
 #include <core/BusManager.h>
 #include <core/SystemError.h>
 #include <core/WoshKernel.h>
 #include <framework/communication/CommunicationEvents.h>


using namespace std;
namespace wosh {
 namespace devices {

 WOSH_REGISTER(wosh::devices::WindowsMobileBundle, "wosh::interfaces::services::CommunicatorService", _WindowsMobile_VERSION, _static_WindowsMobileBundle1 )
 WOSH_REGISTER(wosh::devices::WindowsMobileBundle, "wosh::interfaces::devices::Phone", _WindowsMobile_VERSION, _static_WindowsMobileBundle2 )
 WOSH_REGISTER(wosh::devices::WindowsMobileBundle, "wosh::BundleGeneric", _WindowsMobile_VERSION, _static_WindowsMobileBundle3 )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

WindowsMobileBundle::WindowsMobileBundle()
	: BundleGeneric() {
	BundleGeneric::setName( _WindowsMobile_NAME, false );
	Properties.update( _KEY_Version, _WindowsMobile_VERSION );

Log.setLevel( LOG_VERBOSE );

	Log(LOG_DEBUG, " Configuring WindowsMobile worker.." );
	this->winWorker = new WindowsMobileImpl( Log );
	this->winWorker->setWinMobileListener(this);

	this->devState = wosh::interfaces::Device::UNKNOWN;

	Log(LOG_DEBUG, " Setting (TelephonyProvider) Credentials [SecurityTokenProvider3rdparty].." );
	CredentialsIO.setAuthorityName("IT-PROVIDER");
	CredentialsIO.setAuthorityChannel(wosh::security::CHANNEL_TELEPHONY);
	CredentialsIO.setAuthenticationMode(wosh::SecurityToken::AUTH_EXTERNAL, "SIM");

	Log(LOG_DEBUG, " Setting default properties and permissions.." );
	Properties.set( _CommunicatorService_KEY_ChannelType,	(int)Notification::TYPE_TEXTUAL, Permission(Permission::Read) );
	Properties.set( _CommunicatorService_KEY_ChannelLink,	(int)Communicator::LINK_SYNCH, Permission(Permission::Read) );
	Properties.set( _CommunicatorService_KEY_Cost,			1.0,Permission::RW_R__R__() );
	Properties.set( _CommunicatorService_KEY_Priority,		35,	Permission::RW_RW_R__() );
	Properties.set( _CommunicatorService_KEY_Reliability,	90,	Permission::RW_RW_R__() );
	Properties.set( _CommunicatorService_KEY_Protocol,		(int)Notification::PROTOCOL_SHELL + (int)Notification::PROTOCOL_HUMAN,	Permission::RW_R__R__() );

	Properties.set( _WindowsMobile_KEY_Manufacturer,	"", Permission( Permission::Read ) );
	Properties.set( _WindowsMobile_KEY_Model,			"", Permission( Permission::Read ) );
	Properties.set( _WindowsMobile_KEY_Revision,		"", Permission( Permission::Read ) );
	Properties.set( _WindowsMobile_KEY_SerialNumber,	"", Permission( Permission::Read ) );
	Properties.set( _WindowsMobile_KEY_SubscriberNumber,"", Permission( Permission::Read ) );

	Properties.set( _Phone_KEY_PhoneNumber,			"", Permission( Permission::Read ) );
	Properties.set( _Phone_KEY_PhoneLinesNumber,	0, Permission( Permission::Read ) );
	Properties.set( _Phone_KEY_PhoneUser,			"wosh", Permission( Permission::RW, Permission::Read, Permission::Read ) );

	Log(LOG_DEBUG, " Registering methods.." );
//	MethodList* mmListContacts = Methods.create<MethodList>( _METHOD_List, "List contacts", Permission::R_XR_XR__() );
//	mmListContacts->setDataFieldExtractor( DataFieldExtractor<ContactInfo>::createFor< DataAdapterMapT<string, ContactInfo> >(this->contacts, "Contacts") );

//	MethodRetrieve* mmRetrieveContacts = Methods.create<MethodRetrieve>( _METHOD_Retrieve, "Retrieve contact(s)", Permission::R_XR_XR__() );
//	mmRetrieveContacts->setItemExtractor( ObjectExtractor<ContactInfo, const std::string&>::createFor< DataAdapterMapT<string, ContactInfo> >(this->contacts, &ContactInfo::getUserID ) );

	MethodMessageResponse* mmSendMessageToUser = Methods.create<MethodMessageResponse>( _CommunicatorService_METHOD_sendmessage, "send message to (wosh) username", Permission(Permission::RX) );
	mmSendMessageToUser->setMethod( this, (MethodMessageResponsePtr)&WindowsMobileBundle::mmDoSendMessageToUser );

	MethodWRESULT* mmDeclareCommunicator = Methods.create<MethodWRESULT>( _CommunicatorService_METHOD_declare, "declare as Communicator", Permission::R_XR_XR__() );
	mmDeclareCommunicator->setMethod( this, (MethodWRESULTPtr)&WindowsMobileBundle::declareCommunicator );

	MethodMessageResponse* mmSendSms = Methods.create<MethodMessageResponse>( _WindowsMobile_METHOD_send_sms, "send sms message", Permission::R_XR_XR__() );
	mmSendSms->setMethod( this, (MethodMessageResponsePtr)&WindowsMobileBundle::mmDoSendSms );

	MethodMessageResponse* mmDialCall = Methods.create<MethodMessageResponse>( _Phone_METHOD_dial, "dial a call", Permission::R_XR_XR__() );
	mmDialCall->setMethod( this, (MethodMessageResponsePtr)&WindowsMobileBundle::mmDoDialCall );

	MethodMessageResponse* mmFakeRecSms = Methods.create<MethodMessageResponse>( _WindowsMobile_METHOD_fake_sms, "fake received sms message", Permission::R_XR_XR__() );
	mmFakeRecSms->setMethod( this, (MethodMessageResponsePtr)&WindowsMobileBundle::mmDoFakeRecSms );

	MethodWRESULT* mmUpdateInfos = Methods.create<MethodWRESULT>( _WindowsMobile_METHOD_udpdate_info, "Update Device informations", Permission::R_XR_XR__() );
	mmUpdateInfos->setMethod( this, (MethodWRESULTPtr)&WindowsMobileBundle::updateInfos );

	MethodMessageResponse* mmShutdownWindows = Methods.create<MethodMessageResponse>( _WindowsMobile_METHOD_shutdown, "shutdown message", Permission::R_XR__R__() );
	mmShutdownWindows->setMethod( this, (MethodMessageResponsePtr)&WindowsMobileBundle::mmDoShutdownWindows );
	mmShutdownWindows->getPermission().setOwner("root");
	mmShutdownWindows->getPermission().setGroup("root");

	setBundleState(Bundle::STATE_CREATED, false);
 }


WindowsMobileBundle::~WindowsMobileBundle() {
	Log(LOG_INFO, " Destroying.." );
	if ( isBundleRunning() ) {
		Log(LOG_WARNING, "~WindowsMobileBundle() : Destroying while Bundle is running! Trying to stop.." );
		bundleStop();
	 }
	delete this->winWorker; this->winWorker = NULL;

	Log(LOG_VERBOSE, ":~WindowsMobileBundle() : Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL

WRESULT WindowsMobileBundle::bundleStart() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STARTING) ) return WRET_ERR_WRONG_STATE;

	BusDevices.setMessageHandler(this);
	WRESULT bus_connected = BusDevices.connect( _Bus_Devices, Bus::ACTION_DEFERRED );
	if ( WFAILED(bus_connected) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "bundleStart", "FAILED#%d Connecting Bus ("_Bus_Devices")..", bus_connected );

	WRESULT bus_connected_sec = BusSecurity.connect( _Bus_Security, Bus::ACTION_DEFERRED );
	if ( WFAILED(bus_connected_sec) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "bundleStart", "FAILED#%d Connecting Bus ("_Bus_Security")..", bus_connected_sec );

	updateInfos();

	// must be called within WINMAIN thread!
	WRESULT ret_sms = this->winWorker->startMonitoringSms();
	if ( WFAILED(ret_sms) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "bundleStart", "FAILED#%d startMonitoringSms()", ret_sms );

	// must be called within WINMAIN thread!
	WRESULT ret_call = this->winWorker->startMonitoringCall();
	if ( WFAILED(ret_call) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "bundleStart", "FAILED#%d startMonitoringCall()", ret_call );

	Log(LOG_VERBOSE, ":bundleStart() : Registering as Device.." );
	wosh::Device* myself = new wosh::Device(*this);
	myself->getInterfaces() = this->Interfaces;
	WoshKernel::getInstance()->devices().registerDevice( myself );

	declareCommunicator();

	setBundleState( Bundle::STATE_STARTED );
	return ret_sms + ret_call;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT WindowsMobileBundle::bundleStop() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STOPPING) ) return WRET_ERR_WRONG_STATE;

	// must be called within WINMAIN thread?
	WRESULT ret_sms = this->winWorker->stopMonitoringSms();
	if ( WFAILED(ret_sms) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "bundleStop", "FAILED#%d stopMonitoringSms()", ret_sms );

	// must be called within WINMAIN thread?
	WRESULT ret_call = this->winWorker->stopMonitoringCall();
	if ( WFAILED(ret_call) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "bundleStop", "FAILED#%d stopMonitoringCall()", ret_call );

	Log(LOG_VERBOSE, ":bundleStop() : UnRegistering as Device.." );
	WoshKernel::getInstance()->devices().unRegisterDevice( this->getName() );

	setBundleState( Bundle::STATE_STOPPED );
	return ret_sms + ret_call;
 }

WRESULT WindowsMobileBundle::bundleDiagnostic() {
	Log(LOG_VERBOSE, ":bundleDiagnostic() : Starting WindowsMobileBundle-Diagnostic" );

	WRESULT ret = BundleGeneric::bundleDiagnostic();

	Log(LOG_INFO, ":bundleDiagnostic() : Finished WindowsMobileBundle-Diagnostic [%d]", ret );
	return ret;
 }

////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES

void WindowsMobileBundle::busMessage( const Message& message, const Bus* source ) {
	if ( message.isEmpty() ) return;
	if ( message.getContent()->isNotification() ) {
		if ( message.getDestination().isBroadcast() )
			return;
		if ( !MessageFilter::isMessageTarget(message, this) )
			return;
		if ( !isBundleRunning() )
			return;
		const Notification* notif = message.getContent()->asNotification();
		WRESULT sent = notify( notif );
		Message* reply = message.replyTo( new Response( _CommunicatorService_RESPONSE_notif_sent, new Variant(notif->getID()), sent ) );
		signMessage(reply);
		BusCore.postMessage(reply);
		if ( WSUCCEEDED(sent) ) {
			Message* reply2 = message.replyTo( new Response( _CommunicatorService_RESPONSE_notif_read, new Variant(notif->getID()), WRET_OK ) );
			signMessage(reply2);
			BusCore.postMessage(reply2);
		 }
		return;
	 }
	DeviceManager::busMessageForBundleDevices( message, source, this, this->Methods, this->Log, this->BusCore, this->BusDevices );
 }

//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENT

////////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENT
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// WM6 EVENTS

void WindowsMobileBundle::phone_CallState( WindowsMobileImpl*, VoiceCall* call_info )
 {
	if ( call_info == NULL ) {
		int lines = this->winWorker->getPhoneLinesNumber();
		Properties.transactionBeginWrite();
		Properties.update( _Phone_KEY_PhoneLinesNumber, lines );
		Properties.transactionEnd();
		return;
	 }
	if ( call_info->getSender_ID().empty() ) {
		// caller id is not available yet
		return;
	 }

	std::string username = getUsernameFromPhoneNumber( call_info->getSender_ID().c_str() );
	if ( username.size() == 0 ) {
		SystemError::raise(this, Log, SystemError::WARNING, 0, "phone_CallState", "CALL#%ld from UNKNOWN PHONE %s : '%s'", call_info->getID(), call_info->getSender_ID().c_str(), call_info->getCallSummary().c_str() );
		return;
	 }
	else
		Log(LOG_VERBOSE, ":phone_CallState() CALL#%ld from %s [%s] : '%s'", call_info->getID(), call_info->getSender_ID().c_str(), username.c_str(), call_info->getCallSummary().c_str() );

	call_info->setSender_User( username );
	call_info->setRecipent_User( Properties.getValueOf(_Phone_KEY_PhoneUser).toString() );

	Message* msg_event = new Message( call_info->clone() );
	msg_event->setDestinationBroadcast(true);
	signMessage(msg_event);
	BusSecurity.postMessage(msg_event);
 }

void WindowsMobileBundle::phone_SmsMessage( WindowsMobileImpl*, const struct sms_info& sms )
 {
	std::string username = getUsernameFromPhoneNumber(sms.phone_number);
	if ( username.size() == 0 ) {
		SystemError::raise(this, Log, SystemError::WARNING, 0, "phone_SmsMessage", "SMS from UNKNOWN PHONE %s : '%s'", sms.phone_number.c_str(), sms.body.c_str() );
		return;
	 }
	else
		Log(LOG_VERBOSE, ":phone_SmsMessage() SMS from %s [%s] : '%s'", sms.phone_number.c_str(), username.c_str(), sms.body.c_str() );

	///@todo EVAL USER/SOURCE vs. SECURITY RULES here

	TextMessage* txtmsg = new TextMessage();
	txtmsg->setSMS();
	txtmsg->setSender_ID( sms.phone_number );
	txtmsg->setSender_User( username );
	txtmsg->setRecipent_ID( Properties.getValueOf(_Phone_KEY_PhoneNumber).toString() );
	txtmsg->setRecipent_User( Properties.getValueOf(_Phone_KEY_PhoneUser).toString() );
	txtmsg->setPlain( sms.body );

	Message* msg_event = new Message( new UserInteraction_Event(txtmsg->getSender_User()) );
	msg_event->setDestinationBroadcast(true);
	signMessage(msg_event);
	BusSecurity.postMessage(msg_event);

	// inject TEXT_MESSAGE into WOSH
	Message* msg_txt = new Message( txtmsg );
	msg_txt->setDestinationBroadcast(true);
	msg_txt->setSource(this);
	CredentialsIO.signMessage( msg_txt, txtmsg->getSender_User(), txtmsg->getSender_ID() );
	BusCore.postMessage(msg_txt);
}

////////////////////////////////////////////////////////////////////////////////////////////////// WM6 EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////// WM6 METHODS

WRESULT WindowsMobileBundle::updateInfos() {
	std::string phone_number;
	WRESULT ret = WindowsMobileImpl::getPhoneNumber( phone_number, 1 );
	fixPhoneNumber(phone_number);
	Properties.transactionBeginWrite();
	Properties.update( _Phone_KEY_PhoneNumber, phone_number );
	Properties.transactionEnd();

	if ( WFAILED(ret) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "updateInfos", "FAILED: Retriving PhoneNumber#1: %s", phone_number.c_str() );
	else
		Log(LOG_VERBOSE, ":updateInfos() PhoneNumber#1: %s", phone_number.c_str() );

	std::map<std::string, std::string> fields;
	ret = WindowsMobileImpl::getDeviceInformations( fields );
	std::map<std::string,std::string>::const_iterator it;
	Properties.transactionBeginWrite();
	for ( it=fields.begin(); it!=fields.end(); it++ ) {
		Properties.update( it->first, it->second );
	 }
	Properties.transactionEnd();

	if ( WSUCCEEDED(ret) )
		Log(LOG_VERBOSE, ":updateInfos() Updated %d fields", fields.size() );
	else
		Log(LOG_WARNING, ":updateInfos() Error Retriving fields [%d]", fields.size() );

	return ret;
 }

///////////////////////////////////////////////////////////////////////////////////////////////// WM6 METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// COMMUNICATOR

WRESULT WindowsMobileBundle::declareCommunicator() {
	Message* msg_event = new Message( new Response( _METHOD_ListProperties, Properties.toTable(false) ) );
	msg_event->setDestinationBus(_Bus_Core);
	msg_event->getDestination().fromString("*:Communication");
	signMessage(msg_event);
	BusCore.postMessage(msg_event);

	std::vector<std::string> users = UserManager::findUserByProperty( "PhoneMobile", Variant() );
	if ( !users.empty() ) {
		vector<string>::const_iterator it;
		for(it=users.begin(); it!=users.end(); it++)
			declareUser(true, *it);
	 }

	return WRET_OK;
 }

WRESULT WindowsMobileBundle::declareUser( bool online, const string& username ) {
	Fact* fact = NULL;
	if ( online )
		fact = new UserPresence_StateChanged( username, User::PRESENCE_ONLINE );
	else
		fact = new UserPresence_StateChanged( username, User::PRESENCE_OFFLINE );

	Message* msg_event = new Message( fact );
	msg_event->setDestinationBroadcast(true);
	signMessage(msg_event);
	BusSecurity.postMessage(msg_event);
	return WRET_OK;
 }

bool WindowsMobileBundle::isUserReachable( const string& wosh_username ) {
	std::string phonenumber = getPhoneNumberFromUsername( wosh_username );
	///@bug should at least validate number
	if ( phonenumber.size() > 8 )
		return true;
	return false;
 }

WRESULT WindowsMobileBundle::sendMessageToUser( const string& wosh_username, const string& message ) {
	std::string phonenumber = getPhoneNumberFromUsername(wosh_username);
	if ( phonenumber.size() == 0 ) {
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "sendMessageToUser", "FAILED retrieving phonenumber of User %s", wosh_username.c_str() );
		return WRET_ERR_PARAM;
	 }
	WRESULT ret = sendMessageTo(phonenumber, message );
	return ret;
 }

WRESULT WindowsMobileBundle::sendMessageTo( const string& user_phonenumber, const string& message ) {
	WRESULT ret = WindowsMobileImpl::sendSms( user_phonenumber, message );
	if ( WFAILED(ret) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "sendMessageTo", "FAILED#%d Sending SMS to %s", ret, user_phonenumber.c_str() );
	else
		Log(LOG_INFO, "sendMessageTo(%s, **) SMS message sent", user_phonenumber.c_str() );
	return ret;
 }

WRESULT WindowsMobileBundle::notify( const Notification* notification, const Message* ) {
	if ( notification == NULL ) return WRET_ERR_PARAM;
	WRESULT ret = WRET_OK;
	if ( notification->isForAnyone() || notification->getRecipent_User().size() == 0 ) {
		// should iterate when group-select or/and ?
		ret = WRET_ERR_INTERNAL;
	 }
	else
		return sendMessageToUser(notification->getRecipent_User(), notification->getPlain() );
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string WindowsMobileBundle::getPhoneNumberFromUsername( const string& wosh_username ) {
	bool found = UserManager::isUser(wosh_username);
	if ( !found ) return "";
	return UserManager::getUserProperty(wosh_username, "PhoneMobile").toString();
 }

std::string WindowsMobileBundle::getUsernameFromPhoneNumber( const string& phone_number ) {
	std::vector<std::string> users = UserManager::findUserByProperty( "PhoneMobile", phone_number );
	if ( users.size() == 0 ) return "";
	return users[0];
 }

void WindowsMobileBundle::fixPhoneNumber( string& phone ) const {
	if (phone.empty() ) return;
	if ( phone[0] != '+' )
		phone = "+" + phone;
	if (  phone[1] != '3' || phone[2] != '9' )
		phone = "+39" + phone.substr(1);
 }

//////////////////////////////////////////////////////////////////////////////////////////////// COMMUNICATOR
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* WindowsMobileBundle::mmDoSendSms( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	Variant dPhone;
	Variant dMessage;
	request->extractArguments(dPhone, dMessage);
	if ( !dPhone.isStringNotEmpty() || !dMessage.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid arguments [phone_number, message]");
	if ( dPhone.asString().size() < 5 )
		return request->replyResponse(WRET_ERR_PARAM, "Bad params");
	fixPhoneNumber(dPhone.asString());
	std::string owner = "";
	if ( requestMessage->getSecurityToken() != NULL )
		owner = requestMessage->getSecurityToken()->getUsername();
	Log(LOG_VERBOSE, "mmDoSendSms(%s) : Sending %d chars by %s", dPhone.asString().c_str(), dMessage.asString().size(), owner.c_str() );
	WRESULT ret = WindowsMobileImpl::sendSms( dPhone.asString(), dMessage.asString() );
	if ( WSUCCEEDED(ret) )
		return request->replyResponse(WRET_OK, "Sent!" );
	return request->replyResponse(ret, "Internal Error!");
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* WindowsMobileBundle::mmDoFakeRecSms( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	Variant dUsername;
	Variant dMessage;
	request->extractArguments(dUsername, dMessage);
	if ( !dUsername.isStringNotEmpty() || !dMessage.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid arguments [username, message]");
	if ( dUsername.asString().size() < 5 )
		return request->replyResponse(WRET_ERR_PARAM, "Bad params");
	fixPhoneNumber(dUsername.asString());
	Log(LOG_VERBOSE, "mmDoFakeRecSms() : Faking message %s by %s", dUsername.asString().c_str(), dMessage.asString().size() );

	sms_info messageStruct;
	messageStruct.phone_number = dUsername.asString();
	messageStruct.body = dMessage.asString();
	messageStruct.priority = 0;
	messageStruct.subject = ""; // ignored from sms
	messageStruct.timestamp = Utilities::std_time();

	phone_SmsMessage( NULL, messageStruct );

	return request->replyResponse(WRET_OK);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* WindowsMobileBundle::mmDoSendMessageToUser( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	Variant dUsername;
	Variant dMessage;
	request->extractArguments(dUsername, dMessage);
	if ( !dUsername.isStringNotEmpty() || !dMessage.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid arguments [username, message]");
	std::string sender = requestMessage->getSecurityToken()->getUsername();
	Log(LOG_VERBOSE, "mmDoSendMessageToUser(%s) : Sending %d chars by %s", dUsername.asString().c_str(), dMessage.asString().size(), sender.c_str() );

	WRESULT ret = sendMessageToUser( dUsername.asString(), dMessage.asString() );

	if ( WSUCCEEDED(ret) )
		return request->replyResponse(WRET_OK, "Sent!" );
	return request->replyResponse(ret, "Internal Error!");
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* WindowsMobileBundle::mmDoDialCall( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	std::string recipent = request->getArgument().toString();
	if ( recipent.size() < 5 )
		return request->replyResponse(WRET_ERR_PARAM, "Bad params");
	Log(LOG_VERBOSE, "mmDoDialCall(%s)", recipent.c_str() );

	fixPhoneNumber(recipent);
	WRESULT ret = WindowsMobileImpl::dialNumber( recipent );

	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* WindowsMobileBundle::mmDoShutdownWindows( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	Log(LOG_VERBOSE, "mmDoShutdownWindows()" );

	WRESULT ret = WRET_OK;
	bool done = WindowsMobileImpl::shutdownWindows();
	if (!done)
		ret = WRET_ERR_INTERNAL;

	return request->replyResponse(ret);
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace devices
}; // namespace wosh
